package com.luxus.dev;

import static com.android.camera.one.v2.core.ResponseListeners.forFrameExposure;
import static com.android.camera.one.v2.core.ResponseListeners.forPartialMetadata;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.android.camera.async.BufferQueue;
import com.android.camera.debug.Log;
import com.android.camera.one.OneCamera.IMetadataResult;
import com.android.camera.one.OneCamera.MetadataListener;
import com.android.camera.one.v2.autofocus.AETriggerResult;
import com.android.camera.one.v2.autofocus.AFTriggerResult;
import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy;
import com.android.camera.one.v2.core.FrameServer;
import com.android.camera.one.v2.core.Request;
import com.android.camera.one.v2.core.RequestBuilder;
import com.android.camera.one.v2.core.RequestBuilder.Factory;
import com.android.camera.one.v2.core.RequestTemplate;
import com.android.camera.one.v2.core.ResourceAcquisitionFailedException;
import com.android.camera.one.v2.imagesaver.ImageSaver;
import com.android.camera.one.v2.photo.ImageCaptureCommandExt;
import com.android.camera.one.v2.photo.MetadataFuture;
import com.android.camera.one.v2.photo.PictureCallbackAdapter;
import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageStream;
import com.google.common.util.concurrent.ListenableFuture;

import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;

public class DebugForCaptureFlash extends ImageCaptureCommandExt implements MetadataListener {
	private static final Log.Tag TAG = new Log.Tag("DebugForCap");
	
	private final int mRepeatingRequestTemplate = CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG;
    private final int mStillCaptureRequestTemplate = CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG;
    private Factory mScanRequestTemplate;
    private List<Factory> mBurst;
	private final boolean mWaitForAEConvergence = true;
    private final boolean mWaitForAFConvergence = true;
    
    private boolean mDetectEnable= false;
    private boolean mShouldFlash = false;
	
    public DebugForCaptureFlash(String flashMode) {
    	if ("on".equals(flashMode)) {
    		mShouldFlash = true;
    	} else if ("auto".equals(flashMode)) {
    		mDetectEnable = true;
    	}
    }
	
	@Override
	public void run(PictureCallbackAdapter pictureCallbackAdapter, ImageSaver imageSaver) throws InterruptedException,
			CameraAccessException, CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
		mBurst = Arrays.asList(mBuilderFactory);
		mScanRequestTemplate = resetFocusExposureModes(mBuilderFactory);
		
		try (FrameServer.Session session = mFrameServer.createExclusiveSession()) {
            try (ImageStream imageStream = mImageReader.createPreallocatedStream(mBurst.size())) {
            	if (mWaitForAEConvergence) {
                    waitForAEConvergence(session, pictureCallbackAdapter);
                }
            	if (mWaitForAFConvergence) {
                    waitForAFConvergence(session);
                }
                captureBurst(session, imageStream, pictureCallbackAdapter, imageSaver);
            } finally {
                // Always reset the repeating stream to ensure AF/AE are not
                // locked when this exits.
                // Note that this may still throw if the camera or session is
                // closed.
                resetRepeating(session);
            }
        } finally {
            imageSaver.close();
        }
	}

	@Override
	public void onMetadataCallback(IMetadataResult result) {
		if (!mDetectEnable) return;
		
		final Long NS_TO_S = 1000000000L;
		Integer iso = result.get(CaptureResult.SENSOR_SENSITIVITY);
		Long exposureTime = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
		Long flashResult = exposureTime * iso / NS_TO_S;
		Log.d(TAG, "luxfluxf iso = "+iso);
		Log.d(TAG, "luxfluxf exposureTime = "+exposureTime);
		Log.d(TAG, "luxfluxf flash result = "+flashResult);
		if (flashResult > 80) {
			mShouldFlash = true;
		} else {
			mShouldFlash = false;
		}
	}

	private void waitForAFConvergence(FrameServer.Session session) throws CameraAccessException,
		    InterruptedException, ResourceAcquisitionFailedException,
		    CameraCaptureSessionClosedException {
		AFTriggerResult afStateMachine = new AFTriggerResult();
		
		RequestBuilder triggerBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate);
		triggerBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest
		        .CONTROL_AF_TRIGGER_START);
		triggerBuilder.addResponseListener(forPartialMetadata(afStateMachine));
		
		RequestBuilder idleBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate);
		idleBuilder.addResponseListener(forPartialMetadata(afStateMachine));
		
		session.submitRequest(Arrays.asList(idleBuilder.build()),
		        FrameServer.RequestType.REPEATING);
		
		session.submitRequest(Arrays.asList(triggerBuilder.build()),
		        FrameServer.RequestType.NON_REPEATING);
		
		// Block until the AF trigger is complete
		afStateMachine.get();
	}
	
	private void waitForAEConvergence(FrameServer.Session session, 
			PictureCallbackAdapter pictureCallbackAdapter) throws CameraAccessException,
		    InterruptedException, ResourceAcquisitionFailedException,
		    CameraCaptureSessionClosedException {
		boolean shouldFlash = mShouldFlash;
		AETriggerResult aeStateMachine = new AETriggerResult();
		
		RequestBuilder triggerBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate);
		triggerBuilder.setParam(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
		        CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
		triggerBuilder.addResponseListener(forPartialMetadata(aeStateMachine));
		
		RequestBuilder idleBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate);
		idleBuilder.addResponseListener(forPartialMetadata(aeStateMachine));
		
		session.submitRequest(Arrays.asList(idleBuilder.build()),
		        FrameServer.RequestType.REPEATING);
		
		session.submitRequest(Arrays.asList(triggerBuilder.build()),
		        FrameServer.RequestType.NON_REPEATING);
		if (shouldFlash) {
			pictureCallbackAdapter.providePreFlashUpdatable().update(null);
		}
		// Wait until the ae state converges to a result.
		aeStateMachine.get();
	}
	
	private void captureBurst(FrameServer.Session session, ImageStream imageStream, 
			PictureCallbackAdapter pictureCallbackAdapter, ImageSaver imageSaver) throws CameraAccessException,
		    InterruptedException, ResourceAcquisitionFailedException,
		    CameraCaptureSessionClosedException {
		List<Request> burstRequest = new ArrayList<>(mBurst.size());
		List<ListenableFuture<TotalCaptureResultProxy>> metadata = new ArrayList<>(mBurst.size());
		boolean first = true;
		for (RequestBuilder.Factory builderTemplate : mBurst) {
		    RequestBuilder builder = builderTemplate.create(mStillCaptureRequestTemplate);
		
		    builder.setParam(CaptureRequest.CONTROL_AF_MODE, CaptureRequest
		            .CONTROL_AF_MODE_CONTINUOUS_PICTURE);
		    builder.setParam(CaptureRequest.CONTROL_CAPTURE_INTENT,
		            CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
		
		    if (first) {
		        first = false;
		        builder.addResponseListener(forFrameExposure(
		        		pictureCallbackAdapter.provideQuickExposeUpdatable()));
		    }
		
		    MetadataFuture metadataFuture = new MetadataFuture();
		    builder.addResponseListener(metadataFuture);
		    metadata.add(metadataFuture.getMetadata());
		
		    builder.addStream(imageStream);
		
		    burstRequest.add(builder.build());
		}
		
		session.submitRequest(burstRequest, FrameServer.RequestType.NON_REPEATING);
		
		for (int i = 0; i < mBurst.size(); i++) {
		    try {
		        ImageProxy image = imageStream.getNext();
		        imageSaver.addFullSizeImage(image, metadata.get(i), null);
		    } catch (BufferQueue.BufferQueueClosedException e) {
		        // No more images will be available, so just quit.
		        return;
		    }
		}
	}
	
	private void resetRepeating(FrameServer.Session session) throws InterruptedException,
		    CameraCaptureSessionClosedException, CameraAccessException,
		    ResourceAcquisitionFailedException {
		RequestBuilder repeatingBuilder = mBuilderFactory.create
		        (mRepeatingRequestTemplate);
		session.submitRequest(Arrays.asList(repeatingBuilder.build()),
		        FrameServer.RequestType.REPEATING);
		
		RequestBuilder triggerCancelBuilder = mBuilderFactory
		        .create(mRepeatingRequestTemplate);
		triggerCancelBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER,
		        CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
		session.submitRequest(Arrays.asList(triggerCancelBuilder.build()),
		        FrameServer.RequestType.NON_REPEATING);
		
		// Some devices (e.g. N6) implicitly lock AE after sending an
		// AE_PRECAPTURE trigger. (see bug: 19265647)
		// The implicit lock is released when a request with
		// INTENT_STILL_CAPTURE is taken.
		
		// However, if we never get to that point (because the command was
		// interrupted before the request for a photo was sent), then we must be
		// sure to cancel this implicit AE lock to resume normal AE behavior.
		// Sending a request for an explicit AE lock (followed, implicitly, by a
		// request from the current repeating request, which has AE lock off)
		// fixes the issue and results in normal AE behavior.
		RequestBuilder hackAETriggerCancelBuilder = mBuilderFactory.create
		        (mRepeatingRequestTemplate);
		hackAETriggerCancelBuilder.setParam(CaptureRequest.CONTROL_AE_LOCK, true);
		
		session.submitRequest(Arrays.asList(hackAETriggerCancelBuilder.build()),
		        FrameServer.RequestType.NON_REPEATING);
	}
	
	private static RequestBuilder.Factory resetFocusExposureModes(RequestBuilder.Factory template) {
        RequestTemplate result = new RequestTemplate(template);
        result.setParam(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        result.setParam(CaptureRequest.CONTROL_AF_MODE,
                CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        result.setParam(CaptureRequest.CONTROL_AF_TRIGGER,
                CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
        result.setParam(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
        return result;
    }
}
